package com.juzipi.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.juzipi.common.constant.Constants;
import com.juzipi.common.model.LoginBody;
import com.juzipi.common.model.Mail;
import com.juzipi.common.utils.CaptchaUtils;
import com.juzipi.common.utils.StringUtils;
import com.juzipi.common.utils.redis.RedisUtils;
import com.juzipi.common.utils.security.SecurityUtils;
import com.juzipi.framework.web.service.MailService;
import com.juzipi.framework.web.service.TokenService;
import com.juzipi.inter.domain.LoginUser;
import com.juzipi.inter.mapper.UserMapper;
import com.juzipi.inter.pojo.User;
import com.juzipi.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @Author juzipi
 * @Date 2021/4/12 14:15
 * @Info 操作账户相关的
 */

@Service
public class UserAccountService extends UserServiceImpl {


    @Autowired
    private UserMapper userMapper;
    @Autowired
    private MailService mailService;
    @Autowired
    private RedisUtils redisUtils;
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private TokenService tokenService;

    //找回密码验证码过期时间
    @Value("${captcha.resetPasswordExpirationTime}")
    private int resetPasswordExpirationTime;
    //注册验证码过期时间
    @Value("${captcha.registerExpirationTime}")
    private int registerExpirationTime;



    /**
     * 注册
     * @param loginBody 登录用户
     * @return 返回一个Object结果，为String或Integer
     */
    @Transactional
    public Object registerUser(LoginBody loginBody) {
        /*
        流程是这样的，前端先获取用户的邮箱(先输入邮箱，再输入账号密码注册)，然后给邮箱发送验证码，
        再带着验证码来提交账号和密码 验证验证码是否正确
         */
        String email = loginBody.getEmail();
        if (!checkEmailUnique(email)) {
            return email;
        }
        //从redis里获取验证码
        Object code = redisUtils.getCacheObject(email);
        //验证码过期
        if (StringUtils.isNull(code)) {
            return Constants.CODE_EXPIRED;
        }
        //验证码错误
        if (!Objects.equals(code,loginBody.getCode())) {
            return Constants.CODE_ERROR;
        }
        //用户名是否唯一
        if (!checkUsernameIsExist(loginBody.getUsername())) {
            return loginBody.getUsername();
        }
        //给密码加密
        User user = new User();
        user.setUsername(loginBody.getUsername());
        user.setPassword(SecurityUtils.encryptPassword(loginBody.getPassword()));
        user.setEmail(email);
        user.setCreateBy(loginBody.getUsername());
        user.setUpdateBy(loginBody.getUsername());
        //返回数据库受影响行数
        return userMapper.insert(user);
    }


    /**
     * 注册前发送验证码
     * @param email 邮箱
     * @return 布尔值
     */
    public Boolean registerCode(String email) {
        //验证邮箱是否唯一
        if (!checkEmailUnique(email)) {
            return false;
        }
        //然后发送验证码
        String captcha = CaptchaUtils.captcha(8);
        redisUtils.setCacheObject(email,captcha,registerExpirationTime,TimeUnit.MINUTES);
        Mail mail = new Mail();
        mail.setSubject("注册验证码");
        mail.setEmail(email);
        mail.setContent("<h2>您的验证码是【" + captcha + "】,请于" + registerExpirationTime + "分钟之内完成注册,切勿将验证码泄露给其他人</h2>");
        mailService.sendHtml(mail);
        return true;
    }


    /**
     * 验证邮箱是否匹配验证码
     * @param email 邮箱
     * @param code 验证码
     */
    public Boolean checkEmailAndCode(String email, String code) {
        if (StringUtils.isEmpty(email) || StringUtils.isEmpty(code)){
            return false;
        }
        Object cacheCode = redisUtils.getCacheObject(email);
        if (StringUtils.isNull(cacheCode)){
            return false;
        }
        return Objects.equals(cacheCode, code);
    }



    /**
     * 修改密码
     * @param username 用户名
     * @param password 新密码
     * @return 受影响行数
     */
    public Integer modifyPassword(String username, String password) {
        User user = userMapper.selectOne(new QueryWrapper<User>().lambda().eq(User::getUsername, username));
        return userMapper.update(user, new UpdateWrapper<User>().lambda().eq(User::getUsername, username).set(User::getPassword, password));
    }


    /**
     * 验证邮箱是否正确并发送邮件
     * @param username 用户名
     * @param email 邮箱
     * @return 布尔值
     */
    public Boolean sendCode(String username, String email) {
        User user = userMapper.selectOne(new QueryWrapper<User>().lambda().eq(User::getUsername, username));
        if (StringUtils.isNotNull(user) && StringUtils.isNotEmpty(user.getEmail())) {
            if (email.equals(user.getEmail())) {
                Mail mail = new Mail();
                //生成验证码
                String captcha = CaptchaUtils.getCaptcha();
                /*
                试了很久也达不到理想的效果，由于这个BCryptPasswordEncoder加密是不可逆的，然而登录必须要明文的密码给UsernamePasswordAuthenticationToken
                它会去给传过去的密码进行加密然后和原来的密文密码比较,
                如果直接传给它加密后的密码 密文 去登陆，它会再次加密导致和原密码匹配不上登陆失败。
                 */
                //把生成的验证码加密后 改为 找回密码 的用户的密码
                userMapper.modifyPasswordByUsername(username, SecurityUtils.encryptPassword(captcha));
                //把生成的验证码存入redis
                redisUtils.setCacheObject(username, captcha, resetPasswordExpirationTime ,TimeUnit.MINUTES);
                //设置收件人
                mail.setEmail(email);
                mail.setSubject("致粗心大意的人:");
                mail.setContent("<h2>您的验证码是【"+ captcha +"】," + resetPasswordExpirationTime + "分钟内有效,请勿将验证码泄露给其他人</h2>");
                mailService.sendHtml(mail);
                return true;
            }
        }
        return false;
    }


    /**
     * 验证验证码正确并执行登录
     * @param username 用户名
     * @param emailCode 邮箱验证码
     * @return 信息
     */
    public String checkCode(String username, String emailCode) {
        User user = userMapper.selectOne(new QueryWrapper<User>().lambda().select(User::getUsername,User::getCode).eq(User::getUsername, username));
        if (StringUtils.isNotEmpty(user.getUsername()) && StringUtils.isNotEmpty(user.getCode())) {
            //根据用户名从redis中取出验证码
            Object captcha = redisUtils.getCacheObject(username);
            if (StringUtils.isNull(captcha)) {
                return Constants.CODE_EXPIRED;
            }

            String code = captcha.toString();
            //如果redis里的code和传来的code相同，通过，执行登录
            if (StringUtils.isNotEmpty(code) && Objects.equals(emailCode, code)) {
                //执行登录
                Authentication authenticate = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getUsername(), code));
                LoginUser loginUser = (LoginUser)authenticate.getPrincipal();
                return tokenService.createToken(loginUser);
            }
        }
        return null;
    }












//    /**
//     * 登录
//     * @param username 用户名
//     * @param password 密码
//     * @return token
//     * @Explain 暂时不使用了
//     */
//    @Deprecated
//    public String login(String username, String password) {
//        Authentication authenticate = null;
//        try {
//            //把用户名密码传给 loadUserByUsername 执行登录，然后根据它返回的结果生成token返回
//            //注意，这样去登录不会触发 登陆成功失败等类 的操作，需要自己手动捕获异常再返回信息
//            authenticate = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
//        } catch (AuthenticationException e) {
//            e.getMessage();
//        }
//        LoginUser loginUser = (LoginUser)authenticate.getPrincipal();
//        return tokenService.createToken(loginUser);
//    }



}
